#include "../../include/MySQLConnector.h"
#include "../../include/Configuration.h"
#include "../../include/Logger.h"
#include <iostream>

using std::cout;
using std::endl;
using std::cerr;

MySQLConnector* MySQLConnector::_pInstance = nullptr;
pthread_once_t MySQLConnector::_once = PTHREAD_ONCE_INIT;

MySQLConnector* MySQLConnector::getInstance() {
    pthread_once(&_once, init);
    return _pInstance;
}

void MySQLConnector::init() {
    _pInstance = new MySQLConnector();
    atexit(destroy);
}

void MySQLConnector::destroy() {
    if (_pInstance) {
        delete _pInstance;
        _pInstance = nullptr;
    }
}

MySQLConnector::~MySQLConnector() {
    if(_con) {
        mysql_close(_con);
    }

    mysql_library_end();
}

MySQLConnector::MySQLConnector() {
    _con = mysql_init(nullptr);
    if (_con == nullptr) {
        LogError("Error: Unable to initialize MYSQL connection.");
        exit(1);
    }

    auto configMap = Configuration::getInstance()->getConfigMap();
    auto it = configMap.find("MYSQL_HOST");
    if (it == configMap.end()) {
        LogError("Error: Missing MYSQL_HOST configuration.");
        exit(1);
    }

    string mysqlHost = it->second;
    it = configMap.find("MYSQL_USER");
    if (it == configMap.end()) {
        LogError("Error: Missing MYSQL_USER configuration.");
        exit(1);
    }

    string mysqlUser = it->second;
    it = configMap.find("MYSQL_PWD");
    if (it == configMap.end()) {
        LogError("Error: Missing MYSQL_PWD configuration.");
        exit(1);
    }

    string mysqlPwd = it->second;
    it = configMap.find("MYSQL_DATABASE");
    if (it == configMap.end()) {
        LogError("Error: Missing MYSQL_DATABASE configuration.");
        exit(1);
    }

    string mysqlDatabase = it->second;
    if (!mysql_real_connect(_con, mysqlHost.c_str(), mysqlUser.c_str(), mysqlPwd.c_str(), mysqlDatabase.c_str(), 0, nullptr, 0)) {
        LogError("Error: Unable connect to MySQL database.");
        exit(1);
    }
}

unique_ptr<MYSQL_RES, void(*)(MYSQL_RES*)> MySQLConnector::executeQuery(const string &query) {
    if (mysql_query(_con, query.c_str()) != 0) {
        LogWarn("Error: Unable to extute query: %s", query.c_str());
    }

    LogDebug("Execute query: %s", query.c_str());

    // 使用智能指针实现result自动释放
    /* mysql_free_result(res); */
    MYSQL_RES* res = mysql_store_result(_con);
    return unique_ptr<MYSQL_RES, void(*)(MYSQL_RES*)>(res, mysql_free_result);
}

